package model;
/**
 * @author      Robert Rill, Zalan Czirjak 
 * @version     2011.0603
 * @since       1.6
 */

public class SolveCube {
		/** Megfelokeppen forgatot es helyre tolt vagatok ahoz, hogy egy kocskat alkossanak.*/
        private Matrix3D [] solutions3D;
        /**hany darabb vagatbol epul fel a kocka.*/
        private int nrPieces;
        /** A vagatok nehezsegi szint szerinti rendezett listaja.*/
        private MatrixCube [] piecesCube;
        /**A kezdetben ures kocka amelyet felepitunk.*/
        private MatrixCube solution;
        /**A kirakos merete.*/
        private int size;
        private MatrixCube [][][] allCubes;
        /**Egy valtozo ami jelzi, hogy mikor talaltuk meg a megoldast.*/
        private boolean end=false;
        
        
        public SolveCube(Matrix3D [] pieces3D, int nrPieces) {
                this.nrPieces = nrPieces;
                size = pieces3D[0].getSize();
                solutions3D = new Matrix3D[nrPieces];
                solution = new MatrixCube(size);
                organizePieces(pieces3D);
                createAllCubes();               
        }
        
        /**
         * Parameterkent megadott vagatokat rendezi egy bizonyos nehezsegi fuggveny segitsegevel.
         * 
         * A nehezsegi fuggveny a vagatok un. illeszthetosegevel kapcsolatos. Egy vagat neheznek szamit 
         * es a lista elejere kerul, ha minnel nehezebb hozzailleszteni egy darabot.
         * 
         * A nehezsegi szinthez az erofuggveny szamitasba veszi azt, hogy hany kicsi kockabol all a vagat es
         * hogy minimalisan hany szinte foglal helyet. - lasd MatrixCube leiras.
         * 
         * @param pieces3D - az eredeti vagatlista, amit rendezunk.
         */
        private void organizePieces(Matrix3D [] pieces3D){
        	 piecesCube = new MatrixCube[nrPieces];
        	 //Keszitunk egy matrixot aminek annyi oszlopa van ahany vagat es 2 sora van.
        	 //Ennek a matrixnak az elso soraban a darabok indexe van.
        	 //A masodik soraban az adott vagat "nehezsegi szinteje".
        	 int[][] temp = new int[nrPieces][2];
        	 for(int i=0; i<nrPieces;i++){
        		 temp[i][0]=i;
        		 temp[i][1]=(int) (pieces3D[i].numLitCubes()*Math.pow(10, pieces3D[i].getLevel()));
        	 }
        	 //A masodik sor szerint rendezzuk a matrixot
        	 for(int i=0; i<nrPieces-1;i++)
        		 for(int j=i+1; j<nrPieces; j++)
        			 if(temp[i][1]<temp[j][1]) {
        				 int id, size;
        				 id=temp[i][0];
        				 size=temp[i][1];
        				 
        				 temp[i][0]=temp[j][0];
        				 temp[i][1]=temp[j][1];
        				 
        				 temp[j][0]=id;
        				 temp[j][1]=size;
        			 }
        	 //A rendezett indexek szerint letrehozzuk a darabok listajat, amit a megoldasnal hasznalunk.
        	 for (int i=0; i < nrPieces; ++i) {
                 piecesCube[i] = new MatrixCube(size);
                 piecesCube[i].setCube(pieces3D[temp[i][0]]);
               //  piecesCube[i].printCube();
        	 }
       
        	 //az elso (es legnehezebb) elemet behelyezzuk a kocka kozepebe.
        	 solutions3D[0] = new Matrix3D(size);
        	 solutions3D[0] = pieces3D[temp[0][0]];
        	 solution.setCube(solutions3D[0]);        	 
        }
        
        private void createAllCubes(){
        	 allCubes = new MatrixCube[nrPieces][24][];
        	 MatrixCube [] cubeListR, cubeListS;
             for (int i=1; i < nrPieces; ++i) {
                     cubeListR = piecesCube[i].listOfCubeRotations();
                     allCubes[i] = new MatrixCube[24][];
                     for (int j=0; j < 24; j++) {
                             cubeListS = cubeListR[j].listOfCubeShifts(solution, size-1, size-1, size-1, size-1, size-1, size-1);
                             int l = 0;
                             while (cubeListS[l]!=null) ++l;
                             allCubes[i][j] = new MatrixCube[l];
                             for (int k=0; k < l; ++k) {
                                     allCubes[i][j][k] = new MatrixCube(size);
                                     allCubes[i][j][k] = cubeListS[k];
                             }                                       
                     }
             }
        }
        
        /**
         * A megoldo rekurziv eljaras, ami megkeresi minden vagatnak a helyet a kirakosban.
         * 
         * Az algoritmus alapotlete az, hogy a legnehezebb vagat kore felepitjuk a kockat. Igy egesz pontosan
         * nem tudjuk, hogy a bepalastolt kockaban konkretan hol is fog kialakulni a kocka, de legalabb
         * a legnehezebb kocka forgatasait es eltolasait is nem kell figyelembe vegyuk a megoldas keresesenel.
         * 
         * A solution palastolt kockahoz mindig ugy adunk hozza vagatokat, hogy az meg valid meretu kocka legyen.
         * Az elso kockat leszamitva ameddig meg nem kapjuk a megeoldast minden vagat mind a 2 forgatasat,
         * es ezek valid eltolasait figyelembe veve keressuk a megoldast.
         * 
         * @param i - hanyadik vagatot probaljuk elhelyezni. (i>0)
         */
        private void solve(int i) {
        	if(i==nrPieces) {
        		//Ha ide belep akkor azt jelenti, hogy az utolso vagatot is sikerult elhelyezni a kockaban.
        		//System.out.println("Megvan a megoldas!");
        		end =true;
        		//Mivel behelyezet elso elemnek, mivel kezdetben nem tudtuk, hogy hol alakul ki a kocka,
        		//nem tudjuk a poziciojat, igy most itt ezt be kell allitanunk.
        		MatrixCube first = new MatrixCube(size);
        		first.setCube(solutions3D[0]);
        		first.setCubePosition(solution.getPozX(), solution.getPozY(), solution.getPozZ());
        		solutions3D[0] = first.getCube();
        		//solution.printAll();
        	}
        	else {
        		//nezzuk a forgatasokat
        		for(int r=0; r<24; r++)
        			//az adott forgatas valid eltolasait
        			for(int s=0; s < allCubes[i][r].length; s++){
        				//megporabljuk beilleszteni a kockaba
        				if (solution.sumCube(allCubes[i][r][s])){
        					//ha sikerult, akkor a kovetkezo vagattal probalkozunk
        					 solve(i+1);
        					 if (end){
        						 //ha mar megkaptuk a megoldast, akkor az i-dik vagatot kimentjuk es kilepunk a fuggvenybol
        						 solutions3D[i] = new Matrix3D(size);
        						 allCubes[i][r][s].setCubePosition(solution.getPozX(), solution.getPozY(), solution.getPozZ());
                                 solutions3D[i] = allCubes[i][r][s].getCube();
        						 return;
        						 }
        					 else
        						 //ha meg nincs meg a megoldas, akkor a felhasznalt vagatot kivesszuk (kivonjuk)
        						 //a kirakando kockabol, hogy egy ujabbal probalkozhassunk kesobb.
        						 solution.differenceLittleCube(allCubes[i][r][s]);
        				}
        			}        			
        	}        
    }
        
        public Matrix3D[] getSolutions3D() {
                solve(1);
                //solution.printAll();
                return solutions3D;
        }

}